#ifndef EDITDICTIONARY_H
#define EDITDICTIONARY_H

#include "hashlist.h"
#include "../parser/string.h"

//-----------------------------------------------------------------
//	non-memory efficient editable dictionary class
//	templatized to work with char or unicode
//-----------------------------------------------------------------
//	Write functions output a CStaticHashList
//-----------------------------------------------------------------

template <class X>
class TEditDictionary
{
public:
	TEditDictionary() : allocedLen(0),writeOffset(0),pWords(0)	{	}
	~TEditDictionary()
	{
		Clear();
	}

	//-----------------------------------------------------------------
	//	clear the dictionary
	//-----------------------------------------------------------------
	void	Clear()
	{
		index.Clear(true);
		if( pWords )
		{
			free(pWords);
			pWords = 0;
			allocedLen = writeOffset = 0;
		}
	}

	//-----------------------------------------------------------------
	//	merge 2 dictionaries
	//-----------------------------------------------------------------
	void	Merge( const TEditDictionary *pDictionary )
	{
		u32					i;
		for( i=0;i<pDictionary->index.GetNItems();i++ )
		{
			AddWord(pDictionary->GetHashByIndex(i),pDictionary->GetWordByIndex(i));
		}
	}

	//-----------------------------------------------------------------
	//	get things at a given index
	//-----------------------------------------------------------------
	HASH	GetHashByIndex( u32 idx ) const
	{
		return index.FindHashByIndex(idx);
	}
	const X	*GetWordByIndex( u32 idx ) const
	{	// item is stored by offset into pWords
		return (const X*)IncPointer(pWords,(u32)index.FindByIndex(idx));
	}

	//-----------------------------------------------------------------
	//	add a word to the dictionary
	//-----------------------------------------------------------------
	bool	AddWord( const X *word )
	{
		return AddWord(MakeStringHash(word),word);
	}
	bool	AddWord( HASH byHash, const X *word )
	{
		u32		length;
		const X	*oldWord;

		if( word==0 )				return false;
		oldWord = FindWord(byHash);
		if( oldWord )
		{	// compare the old word against the new word
			// return true if they are the same, false otherwise
			return (STRING::Compare(word,oldWord)==0);
		}

		// add the word by hash using write offset as the pointer
		index.AddByHash(byHash,(void*)writeOffset);

		// get the length of the word
		length = (STRING::GetLength(word) + 1)*sizeof(X);

		// make sure there's enough room
		while( writeOffset+length > allocedLen )
		{
			allocedLen = (allocedLen==0)?(4096):(allocedLen*2);
			pWords = (X*)realloc(pWords,allocedLen);
		}

		// copy the word into the word memory
		memcpy(IncPointer(pWords,writeOffset),word,length);
		writeOffset += length;
		return true;
	}

	//-----------------------------------------------------------------
	//	delete a word from the dictionary
	//-----------------------------------------------------------------
	bool	DeleteWord( HASH byHash )
	{
		u32				idx,len,i;
		X				*pString;
		CHashListItem	*pItems;

		if( index.ItemExists(byHash,&idx) )
		{
			// find the string address
			idx = (u32)index.FindByIndex(idx);
			pString = IncPointer(pWords,idx);
			len = (STRING::GetLength(pString)+1)*sizeof(X);

			// remove from memory
			writeOffset -= len;
			memmove(pString,IncPointer(pString,len),writeOffset-idx);

			// correct all offsets in hashlist
			pItems = (CHashListItem*)index.GetData();
			for( i=0;i<index.GetNItems();i++ )
			{
				if( pItems[i].data > (void*)idx )
				{
					pItems[i].data = IncPointer(pItems[i].data,-len);
				}
			}

			// finally delete the item
			index.RemoveByHash(byHash);
			return true;
		}
		return false;
	}

	//-----------------------------------------------------------------
	//	find a word in the dictionary
	//-----------------------------------------------------------------
	const X	*FindWord( HASH byHash ) const
	{
		u32		idx;
		if( index.ItemExists(byHash,&idx) )
			return GetWordByIndex(idx);
		return 0;
	}

	//-----------------------------------------------------------------
	// functions to write out the dictionary
	//-----------------------------------------------------------------
	u32		GetIndexLength() const
	{
		return index.GetDataSize();
	}
	u32		GetDataLength() const
	{
		return writeOffset;
	}
	u32		GetTotalLength( bool includeStructure=true ) const
	{
		u32		ret = (includeStructure)?sizeof(CStaticHashList):0;
		return ret+GetIndexLength()+GetDataLength();
	}
	void	Write( void *base, CStaticHashList *pList ) const
	{
		u32		indexOffset = sizeof(CStaticHashList);
		u32		wordOffset  = indexOffset + GetIndexLength();
		Write(base,pList,indexOffset,wordOffset);
	}
	void	Write( void *base, CStaticHashList *pList, u32 &indexOffset, u32 &wordOffset ) const
	{
		CHashListItem	*pItem;
		u32				i;

		// setup the CStaticHashList
		pList->SetData(index.GetNItems(),(CHashListItem*)indexOffset);

		// copy and correct the items...
		pItem = (CHashListItem*)IncPointer(base,indexOffset);
		memcpy(pItem,index.GetData(),index.GetDataSize());
		for( i=0;i<index.GetNItems();i++ )
		{	// increase the item's offset by wordOffset
			pItem[i].data = IncPointer(pItem[i].data,wordOffset);
		}

		// now copy the strings
		memcpy(IncPointer(base,wordOffset),pWords,writeOffset);

		// increment the offsets
		wordOffset  += writeOffset;
		indexOffset += GetIndexLength();
	}

	void	Read( const void *base, const CStaticHashList *pList )
	{
		CHashListItem	*pItem;
		u32				i;
		Clear();
		pItem = (CHashListItem*)IncPointer(base,(u32)pList->GetData());
		for( i=0;i<pList->GetNItems();i++ )
		{
			AddWord(pItem[i].hashValue,(X*)IncPointer(base,(u32)pItem[i].data));
		}
	}

protected:
	CHashList	index;

	u32			allocedLen;
	u32			writeOffset;
	X			*pWords;
};

#endif
